package com.jty.lib_base.oss

import android.content.Context
import android.os.Looper
import com.alibaba.sdk.android.oss.*
import com.alibaba.sdk.android.oss.callback.OSSCompletedCallback
import com.alibaba.sdk.android.oss.common.OSSLog
import com.alibaba.sdk.android.oss.common.auth.OSSCredentialProvider
import com.alibaba.sdk.android.oss.common.auth.OSSStsTokenCredentialProvider
import com.alibaba.sdk.android.oss.model.PutObjectRequest
import com.alibaba.sdk.android.oss.model.PutObjectResult
import com.blankj.utilcode.util.NetworkUtils
import com.jty.lib_base.api.ApiService
import com.jty.lib_base.base.BaseBean
import com.jty.lib_base.data.bean.OssBean
import com.jty.lib_base.data.bean.OssParams
import com.jty.lib_base.extension.ApiSubscriptHelper
import com.jty.lib_base.net.RetrofitClient
import com.jty.lib_base.utils.RxThreadHelper
import com.orhanobut.logger.Logger
import io.reactivex.Observable
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import java.util.concurrent.LinkedBlockingDeque
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit


/**
 * author: zhouyh
 * created on: 2022/3/18 3:53 下午
 * description: 文件上传oss
 */
class UploadOss {

    private var mOss: OSS? = null
    private var ossInfo: OssBean? = null
    private val ERR_COUNT_MAX = 2
    private var m_err_count = 0
    private var isUploading = false
    private var getKeyStartTime: Long = 0

    companion object{
        @Volatile
        private var uploadOss: UploadOss?=null
        var mContext: Context?=null

        //用SynchronousQueue替代LinkedBlockingDeque可以达到最大并发量
        private val THREAD_POOL_EXECUTOR = ThreadPoolExecutor(Runtime
            .getRuntime().availableProcessors(), Runtime.getRuntime().availableProcessors() * 2, 30,
            TimeUnit.SECONDS, LinkedBlockingDeque())


        //token失效或已在其他账号登录
        const val HTTP_TOKEN_OUT = 2


        fun getInstance(): UploadOss? {
            if (uploadOss == null) {
                synchronized(UploadOss::class.java) {
                    if (uploadOss == null) {
                        uploadOss = UploadOss()
                    }
                }
            }
            return uploadOss
        }
    }

    fun init(context: Context) {
        mContext = context
    }





    fun clear() {
        if (uploadOss != null) {
            uploadOss = null
        }
    }


    fun isUploading(): Boolean {
        return isUploading
    }

    fun setUploading(uploading: Boolean) {
        isUploading = uploading
    }

    /**
     * 更新Token
     * @param
     */
    private fun initOSS(
        `object`: String?,
        localFile: String,
        listener: OnUploadListener?,
        ossParams: OssParams?,
    ) {
        THREAD_POOL_EXECUTOR.execute(Runnable {
            val conf = ClientConfiguration()
            conf.connectionTimeout = 15 * 1000 // connction time out default 15s
            conf.socketTimeout = 15 * 1000 // socket timeout，default 15s
            conf.maxConcurrentRequest = 5 // synchronous request number，default 5
            conf.maxErrorRetry = 2 // retry，default 2
            OSSLog.enableLog() //write local log file ,path is SDCard_path\OSSLog\logs.csv
            val credentialProvider: OSSCredentialProvider = OSSStsTokenCredentialProvider(
                ossInfo?.accessKeyId,
                ossInfo?.accessKeySecret,
                ossInfo?.securityToken)
            mOss = OSSClient(mContext, ossInfo?.endPoint, credentialProvider, conf)
            upload2OSS(`object`, localFile, listener, ossParams)
        })
    }


    /**
     *
     * @param object 上传后服务器地址
     * @param localFile 本地文件地址
     * @param listener  监听上传事件
     */
    fun upload2OSS(
        `object`: String?,
        localFile: String,
        listener: OnUploadListener?,
        ossParams: OssParams?,
    ) {
        Logger.i("UploadOss oss上传文件开始localFile=$localFile")
        if (!NetworkUtils.isConnected()) {
            m_err_count = 0
            if (listener != null) {
                Logger.i("UploadOss oss上传文件失败 -- 没有网络")
                listener.uploadFail(null, localFile, ossParams)
            }
            return
        }
        if (mOss == null || ossInfo == null) {
            Logger.i("UploadOss oss上传文件 -- mOSS=null")
            m_err_count++
            getOssConf( `object`, localFile, listener, ossParams)
            return
        }
        if (System.currentTimeMillis() - getKeyStartTime >= 50 * 60 * 1000) { //key超时，重新拉取
            Logger.i("UploadOss oss上传文件 -- 马上超时，重新获取配置")
            m_err_count = 0
            getOssConf(`object`, localFile, listener, ossParams)
            return
        }
        THREAD_POOL_EXECUTOR.execute(Runnable {
            val startTime = System.currentTimeMillis()
            setUploading(true)
            val put = PutObjectRequest(ossInfo?.bucketName, `object`, localFile)
            mOss!!.asyncPutObject(put,
                object : OSSCompletedCallback<PutObjectRequest, PutObjectResult?> {
                    override fun onSuccess(request: PutObjectRequest, result: PutObjectResult?) {
                        Observable.just(1)
                            .observeOn(AndroidSchedulers.mainThread())
                            .subscribe(object : Observer<Int?> {
                                override fun onSubscribe(d: Disposable) {}
                                override fun onNext(t: Int) {
                                    setUploading(false)
                                    Logger.i("UploadOss oss上传文件 --  花费的时间time = " + (System.currentTimeMillis() - startTime))
                                    m_err_count = 0
                                    //封装上传路径
                                    val serverUrl: String =
                                        ossInfo?.domain.toString() + request.objectKey
                                    if (listener != null) {
                                        Logger.i("UploadOss oss上传文件 -- 成功:$serverUrl")
                                        listener.uploadSuccess(serverUrl,
                                            request.uploadFilePath,
                                            ossParams)
                                    }
                                }
                                override fun onError(e: Throwable) {}
                                override fun onComplete() {}
                            })
                    }

                    override fun onFailure(
                        request: PutObjectRequest,
                        clientExcepion: ClientException,
                        serviceException: ServiceException,
                    ) {
                        Observable.just(1)
                            .observeOn(AndroidSchedulers.mainThread())
                            .subscribe(object : Observer<Int?> {
                                override fun onSubscribe(d: Disposable) {}
                                override fun onNext(integer: Int) {
                                    setUploading(false)
                                    Logger.i("UploadOss oss上传文件失败")
                                    // Request exception
                                    if (clientExcepion != null) {
                                        Logger.i("UploadOss oss上传文件失败 clientExcepion --》")
                                        // Local exception, such as a network exception
                                        clientExcepion.printStackTrace()
                                        //一般是传的参数错误，不需要计数，直接返回失败
                                        Logger.i("UploadOss oss上传文件失败 clientExcepion --》2")
                                        m_err_count = 0
                                        listener!!.uploadFail(null, localFile, ossParams)
                                        clear()
                                        return
                                    }
                                    if (serviceException != null) {
                                        Logger.i("UploadOss oss上传文件失败 serviceException --》")
                                        // Service exception 拒绝访问 Token失败重新获取
                                        if ("AccessDenied" == serviceException.errorCode || "InvalidAccessKeyId" == serviceException.errorCode || "InvalidBucketName" == serviceException.errorCode) {
                                            Logger.i("UploadOss oss上传文件失败 -- 拒绝访问")
                                        }
                                        Logger.i("ErrorCode ${serviceException.errorCode}")
                                        Logger.i("RequestId ${serviceException.requestId}")
                                        Logger.i("HostId ${serviceException.hostId}")
                                        Logger.i("RawMessage ${serviceException.rawMessage}")
                                    }
                                    m_err_count++
                                    if (m_err_count >= ERR_COUNT_MAX) {
                                        Logger.i("UploadOss oss上传文件失败 -- 上传次数大于最大次数")
                                        m_err_count = 0
                                        listener!!.uploadFail(null, localFile, ossParams)
                                    } else {
                                        Logger.i("UploadOss oss上传文件失败 -- 再次获取OSS配置")
                                        //upload2OSS(object, localFile, listener);
                                        clear()
                                        getOssConf(`object`, localFile, listener, ossParams)
                                    }
                                }
                                override fun onError(e: Throwable) {}
                                override fun onComplete() {}
                            })
                    }
                })
        })
    }

    /**
     * 重新获取Token
     * @param isDialog
     * @param cancelable
     */
    fun getOssConf(
        `object`: String?,
        localFile: String,
        listener: OnUploadListener?,
        ossParams: OssParams?,
    ) {
        RetrofitClient.getInstance().create(ApiService::class.java)
            .getOssConf()
            .compose(RxThreadHelper.rxSchedulerHelper())
            .subscribe(object:ApiSubscriptHelper<BaseBean<OssBean>>(){
                override fun onResult(t: BaseBean<OssBean>) {
                    val thread1 = Looper.getMainLooper().thread
                    val thread2 = Thread.currentThread()
                    Logger.i("m1:-----getOssConf onNext主线程name: " + thread1.name)
                    Logger.i("m1:-----getOssConf onNext当前线程name: " + thread2.name)
                    if (t.code === 0) {
                        m_err_count = 0
                        getKeyStartTime = System.currentTimeMillis()
                        ossInfo = t.data
                        initOSS(`object`, localFile, listener, ossParams)
                    } else {
                        if (t.code === HTTP_TOKEN_OUT) {
                            m_err_count = 0
                            listener!!.uploadFail("-1", localFile, ossParams)
                        } else {
                            m_err_count++
                            if (m_err_count >= ERR_COUNT_MAX) {
                                Logger.i("UploadOss oss上传文件 -- 获得OSS配置失败，code = " + t.code
                                        .toString() + ", 次数大于最大次数")
                                m_err_count = 0
                                listener!!.uploadFail(null, localFile, ossParams)
                            } else {
                                Logger.i("UploadOss oss上传文件 -- 获得OSS配置失败，code = " + t.code
                                        .toString() + ", msg = " + t.msg + ", 再次获取")
                                getOssConf(`object`, localFile, listener, ossParams)
                            }
                        }
                    }
                }

                override fun onFailed(msg: String?) {
                    Logger.i("UploadOss oss上传文件 -- 从后台获得oss配置失败--$msg")
                    m_err_count++
                    if (m_err_count >= ERR_COUNT_MAX) {
                        Logger.i("UploadOss oss上传文件 -- 获得OSS配置失败次数大于最大次数")
                        m_err_count = 0
                        listener!!.uploadFail(null, localFile, ossParams)
                    } else {
                        Logger.i("UploadOss oss上传文件 -- 获得OSS配置失败，再次获取")
                        getOssConf( `object`, localFile, listener, ossParams)
                    }
                }
            })


    }

    interface OnUploadListener {
        fun uploadSuccess(uploadFile: String?, localFile: String, ossParams: OssParams?): String?
        fun uploadFail(uploadFile: String?, localFile: String, ossParams: OssParams?): String?
    }
}